package element.synt;

import ast.*;
import element.base.WordType;
import element.synt.FuncDef;
import element.base.BaseNode;
import element.base.Synt;
import element.base.Word;

import java.util.List;

public class ClassDef extends Synt {
    @Override
    public AstItem toAst() {
        ast.ClassDef classDef = new ast.ClassDef();
        classDef.className = ((Word) getNodes().get(1)).getContent();

        ClassInner inner = (ClassInner) getNodes().get(3);

        for (BaseNode node: inner.getNodes()) {
            if (node.isSynt(Decl.class)) {
                Decl decl = (Decl) node;

                List<AstItem> declAsts = decl.toAsts();

                for (AstItem declAst: declAsts) {
                    LValStatement _declAst = (LValStatement) declAst;
                    classDef.classAttrs.add(_declAst.LVal);

                    if (_declAst.RVal != null) {
                        classDef.attrAssign.add(declAst);
                    }
                }
            } else if (node.isSynt(Annotate.class)) {
                Annotate annotate = (Annotate) node;
                AnnotateType annotateType = (AnnotateType) annotate.getNodes().get(1);
                Word aType = annotateType.findFirst();
                ast.FuncDef funcDef = new ast.FuncDef();
                if (aType.isWord(WordType.NACTK)) {
                    FunctionSignature head = new FunctionSignature();
                    head.signatureType = FunctionSignature.SignatureType.CLASS_CONSTRUCTOR;
                    funcDef.funcSignature = head;
                    funcDef.funcBlock = new BlockStatement();
                } else {
                    FunctionSignature head = new FunctionSignature();
                    head.signatureType = FunctionSignature.SignatureType.CLASS_CONSTRUCTOR;
                    for (AstItem item: classDef.classAttrs) {
                        DataSignature attr = (DataSignature) item;
                        if (attr.type == DataSignature.DataType.PRIMARY) {
                            PrimaryDataSignature _attr = (PrimaryDataSignature) attr;
                            PrimaryDataSignature pds = new PrimaryDataSignature();
                            pds.type = _attr.type;
                            pds.dataType = _attr.dataType;
                            pds.name = _attr.name;
                            head.parameters.add(pds);
                        } else {
                            ComplexDataSignature _attr = (ComplexDataSignature) attr;
                            ComplexDataSignature cds = new ComplexDataSignature();
                            cds.type = _attr.type;
                            cds.dataOperated = _attr.dataOperated;
                            cds.opType = ComplexDataSignature.operationType.ARRAY_PARAM;
                            int dimCount = _attr.operation.size();
                            for (int i = 0; i < dimCount - 1; i++) {
                                PrimaryDataSignature defaultDim = new PrimaryDataSignature();
                                defaultDim.value = -1;
                                cds.operation.add(defaultDim);
                            }
                            AstItem lastDim = _attr.operation.getLast();
                            cds.operation.add(lastDim);
                            head.parameters.add(cds);
                        }
                    }
                    funcDef.funcSignature = head;

                    funcDef.funcBlock = new BlockStatement();
                    for (AstItem item: classDef.classAttrs) {
                        DataSignature attr = (DataSignature) item;
                        String attrName;
                        if (attr.type == DataSignature.DataType.PRIMARY) {
                            PrimaryDataSignature pds = (PrimaryDataSignature) attr;
                            attrName = pds.name;
                        } else {
                            ComplexDataSignature cds = (ComplexDataSignature) attr;
                            PrimaryDataSignature name = (PrimaryDataSignature) cds.dataOperated;
                            attrName = name.name;
                        }

                        PrimaryDataSignature thisTK = new PrimaryDataSignature();
                        thisTK.type = DataSignature.DataType.PRIMARY;
                        thisTK.name = "this";

                        PrimaryDataSignature nameBar = new PrimaryDataSignature();
                        nameBar.type = DataSignature.DataType.PRIMARY;
                        nameBar.name = attrName;

                        ComplexDataSignature lVal = new ComplexDataSignature();
                        lVal.type = DataSignature.DataType.COMPLEX;
                        lVal.opType = ComplexDataSignature.operationType.CLASS_ATTR;
                        lVal.dataOperated = thisTK;
                        lVal.operation.add(nameBar);

                        Expression rVal = new Expression();
                        rVal.operator = Expression.OperatorType.OP_ATOM;
                        rVal.operands.add(nameBar);

                        LValStatement statement = new LValStatement();
                        statement.LVal = lVal;
                        statement.RVal = rVal;
                        statement.type = Statement.StatementType.ASSIGN_STMT;
                        statement.assignType = LValStatement.AssignType.NORMAL;

                        ((BlockStatement) funcDef.funcBlock).statements.add(statement);
                    }
                }
                classDef.classConstructors.add(funcDef);
            } else if (node.isSynt(ConstructorDef.class)) {
                ConstructorDef constructorDef = (ConstructorDef) node;
                classDef.classConstructors.add(constructorDef.toAst());
            } else if (node.isSynt(FuncDef.class)) {
                FuncDef funcDef = (FuncDef) node;
                classDef.classMethods.add(funcDef.toAst(true));
            }
        }

        return classDef;
    }
}
